/**
 * File Management Class
 */

package Utilities;

import java.io.File;
import java.io.FileInputStream;
import java.io.BufferedReader;
import java.io.DataInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.Vector;

public class FileManagement {
	
	//Read file names/path/etc. from a directory
	private static Vector<File> ReadFromDirectory(String Directory) {		  
		File Folder = new File(Directory);
		File[] FileList = Folder.listFiles();
		
		Vector<File> ReturnList = null;
		
		if (FileList!=null){
			//Create return file list
			ReturnList = new Vector<File>();
			
			for (int i = 0;i<FileList.length;++i) {
				if (FileList[i].isFile()) {
					//Add File to Vector
					ReturnList.add(FileList[i]);
				} else if(FileList[i].isDirectory()) {
					//Create new Vector and Add Files recursively
					Vector<File> TempList = new Vector<File>();
					String TempDirectory = FileList[i].toString();
					
					TempList = ReadFromDirectory(TempDirectory);
					ReturnList.addAll(TempList);
				}
			}
		}
		
		return ReturnList;
	}
	
	private static String KMPSearch(String Argument, String Text, StringBuffer NewText) {
        String ReturnText = null;

		//lowercase the text
		Text = new StringBuffer().append(Text.toLowerCase()).toString();

        int M = Argument.length();
        int N = Text.length();

        //preprocess
        int[] next = new int [M];
        int i = 0, j = -1;
        next[0] = - 1;

        while(i < M - 1) {
            if(j == -1 || Argument.charAt(i) == Argument.charAt(j)) {
                i++; j++;
                if(Argument.charAt(i) != Argument.charAt(j)) next[i] = j;
                else                                         next[i] = next[j];
            }
            else j = next[j];
            //System.out.println(i + " " + next[i]);
        }

        i = j = 0;
        while(i < N && j < M) {
            if(j == -1 || Argument.charAt(j) == Text.charAt(i)) {
                i++;
                j++;
            }
            else j = next[j];
        }
        if(j == M) {
             //found, return text
            StringBuffer sb = new StringBuffer();

            //prepare string
            int start;
            if (i-50>=0) start=i-50; else start=0;

            sb.append(Text, start, i-M);
            sb.append("<b>");
            sb.append(Argument);
            sb.append("</b>.. ");
            ReturnText = sb.toString();
            
            NewText.append(Text.substring(i));
        }

        return ReturnText;

    }
	
	//Compute occurence heuristic table 
	//return the rightmost character position on string
	//return -1 if character does not occure in the pattern
	private static int[] BMPrepareBadCharactersShift(String Needle) {
		int [] ReturnArray = new int[26];
		int Length = Needle.length();
		
		//fill array with dummy
		for(int i=0;i<26;++i) ReturnArray[i]=-1;
		
		//fill array with rightmost position
		for(int i=0;i<Length;++i) {
			Character c = new Character(Needle.charAt(i));
			int num = c - '0' - 49;
			if(num>=0&&num<=26) ReturnArray[num]=i; 
		}
		
		return ReturnArray;
	}
	
	private static int[] BMComputePrefix(String Needle) {
		int Length = Needle.length();
		int [] ReturnArray = new int[Length];
		
		//Prepare variables
		int temp = 0;
		ReturnArray[0] = 0;
		
		 
		 for(int i = 1; i < Length; ++i) {
		     while (temp > 0 && Needle.charAt(temp) != Needle.charAt(i)) temp = ReturnArray[temp-1];
		     if (Needle.charAt(temp) == Needle.charAt(i)) temp++;
		     ReturnArray[i] = temp;
		 }
		
		return ReturnArray;
	}
	
	//Compute match heuristic table
	//return table contain shift distance if a mismatch at position length-1 occurs
	private static int[] BMPrepareGoodSuffixShift(String Needle) {
		int Length = Needle.length();
		int [] ReturnArray = new int[Length+1];
		
		//reverse string
		String ReverseNeedle = new StringBuffer(Needle).reverse().toString();
		
		//compute prefixs
		int [] PrefixNormal = BMComputePrefix(Needle);
		int [] PrefixReverse = BMComputePrefix(ReverseNeedle);
		
		//prepare good suffix
		for(int i=0;i<=Length;++i) ReturnArray[i] = Length - PrefixNormal[Length-1];
		
		for(int j=0;j<Length;++j) {
			int k,l;
			k = Length - PrefixReverse[j];
			l = j - PrefixReverse[j] + 1;
			
			if(ReturnArray[k]>l) ReturnArray[k]=l;
		}
		
		return ReturnArray;
	}
	
	//Search string using boyer-moore algorithm
	//return the string line if found, null if not found
	private static String BMSearch(String Argument, String Text, StringBuffer NewText) {
		String ReturnText = null;
		int argslen,textlen;
		
		//lowercase the text
		Text = new StringBuffer().append(Text.toLowerCase()).toString();
	
		//calc string sizes
		argslen = Argument.length();
		textlen = Text.length();
		
		//simple checks
		if(argslen==0) {
			ReturnText=Text;
		}
		else if(argslen!=0 && textlen!=0){
			int [] BadCharacters = BMPrepareBadCharactersShift(Argument);
			int [] GoodSuffixs = BMPrepareGoodSuffixShift(Argument);
			
			//search text
			int i = 0;
			while (i<=(textlen-argslen)) {
				int j = argslen;
				//decrement j when character are the same
				while(j>0 && Argument.charAt(j-1)==Text.charAt(i+j-1)) j--;
				
				//if not found
				if(j>0) {
					Character c = new Character(Text.charAt(i+j-1));
					int k;
					
					if(Character.isLetter(c)) {
						int num = c - '0' - 49;
						k = BadCharacters[num];
					} else {k=-1;}
					
					int m = j-k-1;

					//shift according table
					if((k<j)&&(m>GoodSuffixs[j])) {i+=m;} 
					else {i+=GoodSuffixs[j];}
					
				} else {
					//found, return text
					StringBuffer sb = new StringBuffer();
					
					//prepare string
					int start;
					if (i-50>=0) start=i-50; else start=0;
					
					sb.append(Text, start, i);
					sb.append("<b>");
					sb.append(Argument);
					sb.append("</b>.. ");
					ReturnText = sb.toString();
					
					NewText.append(Text.substring(i+argslen));
			
					break;
				}
			}
		}
		
		return ReturnText;
	}
	
	public static SearchResults Search(String Directory, int SearchType, int Algorithm, String Arguments) {
		SearchResults Results = null;
		Vector<File> FileVector = ReadFromDirectory(Directory);
		Vector<String> ArgumentsVector = Parser.ParseText(Arguments);
		
		//directory and arguments validation
		if((FileVector!=null)&&(ArgumentsVector!=null)){
			Results = new SearchResults();
			int num = FileVector.size();
			
			//declare file input variables
			FileInputStream fis = null;
			DataInputStream dis = null;
			BufferedReader br = null;
			String input,title;
			int argslen = ArgumentsVector.size();
			
			//search all files
			for(int i=0;i<num;++i) {
				try {			
					//Search variable
					int NumFound = 0;
					StringBuffer TempResult = new StringBuffer();
					Boolean Found = false;
					String Result = null;
					
					//open file
					fis = new FileInputStream(FileVector.elementAt(i));
					dis = new DataInputStream(fis);
					br = new BufferedReader(new InputStreamReader(dis));
					
					input=br.readLine();
					title=input;
					
					//Only check the title if it were a quick search
					if(SearchType==0){
						for(int j=0;j<argslen;++j) {
							if(Algorithm==0) {
								Result=KMPSearch(ArgumentsVector.elementAt(j), title, TempResult);
							} else if(Algorithm==1) {
								Result=BMSearch(ArgumentsVector.elementAt(j), title, TempResult);
							}
							//add number if results for word found
							if(Result!=null) {NumFound++;}
						}
						//if all words in phrase were found, then found
						if(NumFound==argslen) {Found=true;}
					} else if(SearchType==1){ //do full search	
						while((!Found)&&(input!=null)){	
							String ResultTemp = null;
							if(Algorithm==0) {
								ResultTemp=KMPSearch(ArgumentsVector.elementAt(NumFound), input, TempResult);
							} else if(Algorithm==1) {
								ResultTemp=BMSearch(ArgumentsVector.elementAt(NumFound), input, TempResult);
							}
					
							//add number if result for word found and repeat
							if(ResultTemp!=null){
								if(Result==null) Result = "";
								Result = Result + ResultTemp;
								NumFound++;
								
								//crop input and reset temporary result
								input = TempResult.toString();
								TempResult = new StringBuffer();
							} else input = br.readLine();//read next line if not found
						
							//exit if all words were found
							if(NumFound==argslen) {Found=true;}
						}
					}
					
					//Save results when found
					if (Found||Result!=null) {
						if(SearchType==0){
							Results.AddSearchResult(FileVector.elementAt(i).toString(), title, "");
						}else if(SearchType==1) {
							Results.AddSearchResult(FileVector.elementAt(i).toString(), title, Result);
						}
					}
					
					//close file
					br.close();
					dis.close();
					br.close();
				} catch (FileNotFoundException e) {
					e.printStackTrace();
				} catch (IOException e) {
					e.printStackTrace();
				} 
			}
		}
		
		return Results;
	}

}
